package com.bucg.cjcloud.gb28181.sip;

import cn.hutool.core.collection.ConcurrentHashSet;
import com.bucg.cjcloud.gb28181.common.entity.Device;
import com.bucg.cjcloud.gb28181.common.entity.ItemEntity;
import com.bucg.cjcloud.gb28181.common.service.DeviceMangeService;
import com.bucg.cjcloud.gb28181.media.cache.PlayCache;
import com.bucg.cjcloud.gb28181.media.conf.HttpClient;
import com.bucg.cjcloud.gb28181.media.conf.MediaProperties;
import com.bucg.cjcloud.gb28181.media.conf.MediaServerConfiguration;
import com.bucg.cjcloud.gb28181.sip.modle.Pair;
import com.bucg.cjcloud.gb28181.sip.support.SNManager;
import com.bucg.cjcloud.gb28181.sip.xmlbean.CmdTypeEnmu;
import com.bucg.cjcloud.gb28181.sip.xmlbean.CommonReq;
import com.thoughtworks.xstream.XStream;
import gov.nist.javax.sdp.MediaDescriptionImpl;
import gov.nist.javax.sdp.SessionDescriptionImpl;
import gov.nist.javax.sdp.TimeDescriptionImpl;
import gov.nist.javax.sdp.fields.AttributeField;
import gov.nist.javax.sdp.fields.ConnectionField;
import gov.nist.javax.sdp.fields.MediaField;
import gov.nist.javax.sdp.fields.OriginField;
import gov.nist.javax.sdp.fields.ProtoVersionField;
import gov.nist.javax.sdp.fields.RepeatField;
import gov.nist.javax.sdp.fields.SessionNameField;
import gov.nist.javax.sdp.fields.TimeField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.sdp.Connection;
import javax.sdp.SdpException;
import javax.sdp.Version;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import javax.sip.header.Header;
import javax.sip.header.HeaderFactory;
import javax.sip.header.SubjectHeader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;

@Service
public class RequestMessageService {
    @Autowired
    private XStream xstream;
    @Autowired
    private SNManager snManager;
    @Autowired
    private MessageHandel messageHandel;
    @Autowired
    private SipServerInfo sipinfo;
    @Autowired
    private HeaderFactory headerFactory;
    @Autowired
    private MediaServerConfiguration mediaServer;
    @Autowired
    HttpClient httpClient;

    @Autowired
    MediaProperties mediaProperties;

    @Autowired
    DeviceMangeService deviceMangeService;

    Set<Integer> prePlay = new ConcurrentHashSet<>();
    Set<Integer> preStop = new ConcurrentHashSet<>();

    public void catalog(Device device) throws ParseException, InvalidArgumentException, SipException {
        CommonReq req = new CommonReq(CmdTypeEnmu.Catalog, snManager.getSN(), device.getDeviceId());
        String message = xstream.toXML(req);
        String realAddr = deviceMangeService.getRealAddr(device.getDeviceId());
        Assert.notNull(realAddr, "设备信息获取失败");
        messageHandel.sendMessage(realAddr, device.getDeviceId(), message);

    }

    public void deviceInfo(Device device) throws ParseException, InvalidArgumentException, SipException {
        CommonReq req = new CommonReq(CmdTypeEnmu.DeviceInfo, snManager.getSN(), device.getDeviceId());
        String message = xstream.toXML(req);
        String realAddr = deviceMangeService.getRealAddr(device.getDeviceId());
        Assert.notNull(realAddr, "设备信息获取失败");
        messageHandel.sendMessage(realAddr, device.getDeviceId(), message);

    }


    public String play(Device device, boolean replay) throws SdpException, ParseException, InvalidArgumentException, SipException {


        String realAddr = deviceMangeService.getRealAddr(device.getDeviceId());
        Assert.notNull(realAddr, "设备信息获取失败");
        ItemEntity item = deviceMangeService.getItem(device.getDeviceId());
        Pair<String, String> hostIpHash = mediaServer.getHostIpHash(realAddr);
        // 没有播放，发起播放请求

        Integer ssrcId = item.getSsrcId();
        boolean b = httpClient.checkSsrr(realAddr, ssrcId);
        //
        if (!b) {
            synchronized (prePlay) {
                boolean realOp = prePlay.add(ssrcId) && !preStop.contains(ssrcId);

                if (realOp) {
                    // if (!replay && PlayCache.SSRC_PLAYING.contains(item.getSsrcId())) return "请不要重复请求";
                    // 清除本地播放会话
                    PlayCache.MEDIA_SERVER_PLAYING.getOrDefault(hostIpHash, new HashSet<>()).remove(ssrcId);
                    PlayCache.SSRC_PLAYING.remove(ssrcId);

                    String sessionDescription = createSessionDescription(hostIpHash, ssrcId);
                    System.out.println(sessionDescription);
                    SubjectHeader header = headerFactory.createSubjectHeader(device.getDeviceId() + ":001," + sipinfo.getDomain() + ":001");
                    List<Header> list = new ArrayList<>();
                    list.add(header);
                    messageHandel.sendMessageINVITE(realAddr, device.getDeviceId(), sessionDescription, list);

                    PlayCache.MEDIA_SERVER_PLAYING.getOrDefault(hostIpHash, new HashSet<>()).add(ssrcId);
                    PlayCache.SSRC_PLAYING.add(ssrcId);
                }
            }
        }
        return "rtmp://" + hostIpHash.getKey() + "/" + mediaProperties.getAppType() + "/" + String.format("%08X", ssrcId);
    }

    public void bye(Device device) throws SdpException, ParseException, InvalidArgumentException, SipException {
        String realAddr = deviceMangeService.getRealAddr(device.getDeviceId());
        Assert.notNull(realAddr, "设备信息获取失败");
        ItemEntity item = deviceMangeService.getItem(device.getDeviceId());
        Integer ssrcId = item.getSsrcId();
        if (!PlayCache.SSRC_PLAYING.contains(ssrcId)) return;

        boolean realOp = false;
        synchronized (prePlay) {
            realOp = preStop.add(ssrcId) && !prePlay.contains(ssrcId);
            if (realOp) {
                SubjectHeader header = headerFactory.createSubjectHeader(device.getDeviceId() + ":001," + sipinfo.getDomain() + ":001");
                List<Header> list = new ArrayList<>();
                list.add(header);
                messageHandel.sendMessageBYE(realAddr, device.getDeviceId());

                Pair<String, String> hostIpHash = mediaServer.getHostIpHash(realAddr);
                PlayCache.MEDIA_SERVER_PLAYING.getOrDefault(hostIpHash, new HashSet<>()).remove(ssrcId);
                PlayCache.SSRC_PLAYING.remove(ssrcId);
            }
        }
    }

    /**
     * String content = "v=0\r\n" +
     * "o=" + SipServerConstant.SIP_PROTOCOL + " 0 0 IN IP4 " + recevieAddr[0] + " \r\n" +
     * "s=Play\r\n" +
     * "c=IN IP4 " + recevieAddr[0] + "\r\n" +
     * "t=0 0\r\n" +
     * "m=video " + recevieAddr[1] + " RTP/AVP 96 98 97\r\n" +
     * "a=recvonly\r\n" +
     * "a=rtpmap:96 PS/90000\r\n" +
     * "a=rtpmap:98 H264/90000\r\n" +
     * "a=rtpmap:97 MPEG4/90000\r\n" +
     * "y=" + ssrc + "\r\n";
     *
     * @param hostIpHash 媒体接受地址
     * @param ssrcId
     * @return
     * @throws SdpException
     */
    private String createSessionDescription(Pair<String, String> hostIpHash, Integer ssrcId) throws SdpException {

        SessionDescriptionImpl session = new SessionDescriptionImpl();
        Version version = new ProtoVersionField();
        version.setVersion(0);
        session.setVersion(version);

        OriginField origin = new OriginField();
        origin.setAddress(sipinfo.getIp());
        origin.setUsername(sipinfo.getUsername());
        origin.setSessionId(0);
        origin.setSessionVersion(0);
        origin.setAddressType("IP4");
        origin.setNettype("IN");
        session.setOrigin(origin);

        SessionNameField nameField = new SessionNameField();
        nameField.setSessionName("Play");
        session.setSessionName(nameField);

        Connection connect = new ConnectionField();
        connect.setAddress(sipinfo.getIp());
        String mediaServer = hostIpHash.getKey();
        connect.setAddress(mediaServer);
        connect.setAddressType("IP4");
        connect.setNetworkType("IN");
        session.setConnection(connect);
        TimeDescriptionImpl impl = new TimeDescriptionImpl();
        Vector<String> RepeatTime = new Vector<>();
        RepeatField field = new RepeatField();

        // impl.setRepeatTimes(RepeatTime);
        TimeField timeField = new TimeField();
        timeField.setStartTime(0);
        timeField.setStopTime(0);
        impl.setTime(timeField);
        Vector<Object> vector = new Vector<>();
        vector.add(impl);
        session.setTimeDescriptions(vector);

        MediaDescriptionImpl mediaDescriptionImpl = new MediaDescriptionImpl();
        MediaField mediaField = new MediaField();
        mediaField.setMediaType("video");
        mediaField.setPort(Integer.parseInt(hostIpHash.getValue()));
        mediaField.setProtocol("RTP/AVP");
        Vector<Object> mediaFormat = new Vector<>();
        mediaFormat.add("96");
        mediaFormat.add("97");
        mediaFormat.add("98");
        mediaField.setMediaFormats(mediaFormat);
        mediaDescriptionImpl.setMedia(mediaField);
        Vector<Object> vector2 = new Vector<>();
        vector2.add(mediaDescriptionImpl);
        session.setMediaDescriptions(vector2);

        Vector<AttributeField> v3 = new Vector<>();
        AttributeField a1 = new AttributeField();
        a1.setValue("recvonly");
        ;
        v3.add(a1);
        AttributeField a2 = new AttributeField();
        a2.setValue("rtpmap:96 PS/90000");
        ;
        v3.add(a2);
        AttributeField a3 = new AttributeField();
        a3.setValue("rtpmap:98 H264/90000");
        ;
        v3.add(a3);
        AttributeField a4 = new AttributeField();
        a4.setValue("rtpmap:97 MPEG4/90000");
        ;
        v3.add(a4);
        mediaDescriptionImpl.setAttributes(v3);

        return session.toString() + "y=" + ssrcId + "\r\n";

    }


}
